import os
import time
import tkinter as tk
from tkinter import ttk, messagebox
from tkinter.filedialog import askdirectory, askopenfilename
from tkinter.messagebox import showinfo

import pyzipper

import constant
from User import User


def run():
    root = tk.Tk()

    # 设置窗口标题
    root.title('密码管理器')

    # 设置窗口大小，位置
    set_window_size_pos(root, 600, 400)

    # root下创建4个按钮：新建、打开、合并、退出，先不实现合并
    create_2_buttons(root)

    root.protocol("WM_DELETE_WINDOW", lambda: root.destroy())

    root.mainloop()


def flash_interface(mode):
    match mode:
        case "普通":
            constant.pending_selected_list = constant.current_user_list
            set_toolbar_frame(constant.main_button_list)
            set_header_frame()
            set_content_frame(constant.current_user_list)
        case "搜索":
            constant.pending_selected_list = constant.search_user_list
            set_toolbar_frame(constant.search_mode_button_list)
            set_header_frame()
            set_content_frame(constant.search_user_list)
        case "选择":
            set_toolbar_frame(constant.select_mode_button_list)
            set_header_frame()
            set_content_frame(constant.pending_selected_list)


def create_zip(input_file, output_zip):
    with pyzipper.AESZipFile(output_zip, 'w', encryption=pyzipper.WZ_AES, compresslevel=9) as f:
        f.setpassword(constant.zip_password.encode('utf-8'))
        f.write(input_file, arcname=os.path.basename(input_file))


def set_toolbar_frame(button_list):
    if isinstance(constant.toolbar_frame, ttk.Frame) and len(constant.toolbar_frame.winfo_children()) != 0:
        for child in constant.toolbar_frame.winfo_children():
            child.destroy()

    for button_name in button_list:
        match button_name:
            case "添加":
                def add_button_clicked():
                    # 隐藏主窗口
                    constant.toolbar_frame.master.withdraw()
                    # 创建输入窗口
                    add_window = tk.Toplevel(constant.toolbar_frame.master)
                    add_window.title('新建信息')
                    set_window_size_pos(add_window, 300, 200)

                    # 如果用户直接关闭窗口（而不是点击确认），也重新启用按钮
                    add_window.protocol("WM_DELETE_WINDOW",
                                        lambda: (add_window.destroy(), constant.toolbar_frame.master.deiconify()))
                    add_window.attributes('-topmost', True)  # 设置为置顶

                    # 配置 grid
                    add_window.columnconfigure(0, weight=1)
                    add_window.columnconfigure(1, weight=3)

                    title = tk.StringVar()
                    username = tk.StringVar()
                    password = tk.StringVar()
                    note = tk.StringVar()

                    # title
                    title_label = ttk.Label(add_window, text="标题")
                    title_label.grid(column=0, row=0, sticky=tk.NS, padx=5, pady=5)

                    title_entry = ttk.Entry(add_window, textvariable=title)
                    title_entry.grid(column=1, row=0, sticky=tk.NS, padx=5, pady=5)

                    # username
                    username_label = ttk.Label(add_window, text="用户名")
                    username_label.grid(column=0, row=1, sticky=tk.NS, padx=5, pady=5)

                    username_entry = ttk.Entry(add_window, textvariable=username)
                    username_entry.grid(column=1, row=1, sticky=tk.NS, padx=5, pady=5)

                    # password
                    password_label = ttk.Label(add_window, text="密码")
                    password_label.grid(column=0, row=2, sticky=tk.NS, padx=5, pady=5)

                    password_entry = ttk.Entry(add_window, textvariable=password)
                    password_entry.grid(column=1, row=2, sticky=tk.NS, padx=5, pady=5)

                    # note
                    note_label = ttk.Label(add_window, text="备注")
                    note_label.grid(column=0, row=3, sticky=tk.NS, padx=5, pady=5)

                    note_entry = ttk.Entry(add_window, textvariable=note)
                    note_entry.grid(column=1, row=3, sticky=tk.NS, padx=5, pady=5)

                    # cancel button
                    cancel_button = ttk.Button(add_window, text="取消",
                                               command=lambda: (add_window.destroy(),
                                                                constant.toolbar_frame.master.deiconify()))
                    cancel_button.grid(column=0, row=4, sticky=tk.NS, padx=25, pady=25)

                    def finish_button_clicked():
                        nonlocal add_window
                        if title.get() == "" or "," in title.get():
                            add_window.withdraw()
                            root = tk.Tk()
                            root.withdraw()  # 隐藏主窗口

                            # 创建一个置顶的 Toplevel 窗口
                            top = tk.Toplevel(root)
                            top.attributes('-topmost', True)
                            top.withdraw()  # 隐藏 Toplevel 窗口

                            # 弹出 messagebox（会继承 Toplevel 的置顶属性）
                            showinfo("错误提示", message="标题不能为空且不能含有','")

                            root.destroy()  # 关闭主窗口
                            add_window.deiconify()
                        else:
                            user = User(title.get(), username.get(), password.get(), note.get())
                            constant.current_user_list.append(user)
                            flash_interface(constant.app_mode[0])

                            add_window.destroy()
                            constant.toolbar_frame.master.deiconify()

                    # finish button
                    finish_button = ttk.Button(add_window, text="确认",
                                               command=finish_button_clicked)
                    finish_button.grid(column=1, row=4, sticky=tk.NS, padx=25, pady=25)

                # 添加按钮
                add_button = ttk.Button(constant.toolbar_frame, text="添加",
                                        command=lambda: add_button_clicked())
                add_button.pack(side="left", expand=True, fill="both")
            case "排序":
                def sort_button_clicked():
                    constant.current_user_list.sort(key=lambda x: x.title)

                    if constant.sort_flag:
                        constant.sort_flag = False
                    else:
                        constant.sort_flag = True
                        constant.current_user_list.reverse()

                    flash_interface(constant.app_mode[0])

                # 排序按钮
                sort_button = ttk.Button(constant.toolbar_frame, text="排序",
                                         command=sort_button_clicked)
                sort_button.pack(side="left", expand=True, fill="both")
                if len(constant.current_user_list) == 0:
                    sort_button.config(state=tk.DISABLED)
                else:
                    sort_button.config(state=tk.NORMAL)
            case "搜索":
                def search_button_clicked():
                    flash_interface(constant.app_mode[1])
                    constant.toolbar_frame.master.withdraw()

                    root = tk.Toplevel(constant.toolbar_frame.master)
                    root.grab_set()
                    root.overrideredirect(True)

                    # root.title("输入")
                    root.resizable(False, False)
                    set_window_size_pos(root, 300, 300)
                    root.attributes("-topmost", True)

                    text = tk.StringVar()
                    # 创建 Style 对象
                    style = ttk.Style()

                    # 配置样式：居中 + 自定义字体
                    style.configure(
                        "CenteredBold.TLabel",
                        justify="center",  # 文本居中
                        font=("微软雅黑", 16, "bold")  # 字体设置
                    )
                    info_label = ttk.Label(root, text="在下方输入搜索关键字", anchor='center',
                                           style="CenteredBold.TLabel")
                    info_label.pack(side="top", expand=True, fill="both")

                    input_entry = ttk.Entry(root, textvariable=text)
                    input_entry.pack(side="top", expand=True, fill="both")

                    button_frame = ttk.Frame(root)
                    button_frame.pack(side="top", expand=True, fill="both")

                    def cancel_button_clicked():
                        root.destroy()
                        constant.search_user_list = []
                        constant.toolbar_frame.master.deiconify()

                    cancel_button = ttk.Button(button_frame, text="取消", command=cancel_button_clicked)
                    cancel_button.pack(side="left", expand=True, fill="both")

                    def finish_button_clicked():
                        if text.get() != '':
                            for user in constant.current_user_list:
                                if text.get().lower() in user.title.lower():
                                    constant.search_user_list.append(user)
                            flash_interface(constant.app_mode[1])

                        root.destroy()
                        constant.search_user_list = []
                        constant.toolbar_frame.master.deiconify()

                    finish_button = ttk.Button(button_frame, text="确认", command=finish_button_clicked)
                    finish_button.pack(side="left", expand=True, fill="both")

                # 搜索按钮
                search_button = ttk.Button(constant.toolbar_frame, text="搜索",
                                           command=search_button_clicked)
                search_button.pack(side="left", expand=True, fill="both")
                if len(constant.current_user_list) == 0:
                    search_button.config(state=tk.DISABLED)
                else:
                    search_button.config(state=tk.NORMAL)
            case "选择":
                def choose_button_clicked():
                    flash_interface(constant.app_mode[2])

                    # 创建样式
                    style = ttk.Style()
                    style.configure('Blue.TFrame', background='blue')

                    def on_kid_click(event):

                        father_label = event.widget.master
                        current_style = father_label.cget("style")

                        if current_style == 'Blue.TFrame':
                            # 若当前为蓝色背景，恢复默认背景
                            father_label.configure(style='TFrame')

                            for label in father_label.winfo_children():
                                label.config(bg="SystemButtonFace")
                            if father_label in constant.selected_list:
                                constant.selected_list.remove(father_label)
                        else:
                            # 点击的标签背景色变蓝
                            father_label.configure(style='Blue.TFrame')

                            for label in father_label.winfo_children():
                                label.config(bg="#00BFFF")

                            if father_label not in constant.selected_list:
                                constant.selected_list.append(father_label)

                        if len(constant.selected_list) != 0:
                            if isinstance(constant.delete_button, ttk.Button):
                                constant.delete_button.config(state=tk.NORMAL)
                        else:
                            if isinstance(constant.delete_button, ttk.Button):
                                constant.delete_button.config(state=tk.DISABLED)

                    for c in constant.content_frame.winfo_children():
                        for kid in c.winfo_children():
                            kid.bind("<Button-1>", on_kid_click)

                # 选择按钮
                select_button = ttk.Button(constant.toolbar_frame, text="选择",
                                           command=choose_button_clicked)
                select_button.pack(side="left", expand=True, fill="both")

                if len(constant.current_user_list) == 0:
                    select_button.config(state=tk.DISABLED)
                else:
                    select_button.config(state=tk.NORMAL)
            case "保存":
                def save_button_clicked():
                    showinfo("选择保存路径", message="请选择保存路径")
                    folder_path = askdirectory()
                    temp_txt_path = os.path.join(folder_path, f"{int(time.time())}.txt")
                    try:
                        with open(temp_txt_path, "w", encoding="utf-8") as f:
                            for user in constant.current_user_list:
                                f.write(user.title + ",")
                                f.write(user.username + ",")
                                f.write(user.password + ",")
                                f.write(user.note + os.linesep)

                        zip_path = os.path.join(folder_path, f'{int(time.time())}.zip')
                        create_zip(temp_txt_path, zip_path)
                        showinfo(message="保存成功")
                        os.remove(temp_txt_path)
                    except Exception:
                        showinfo(message="保存文件时出错")

                # 保存按钮
                exit_select_button = ttk.Button(constant.toolbar_frame, text="保存", command=save_button_clicked)
                exit_select_button.pack(side="left", expand=True, fill="both")
                if len(constant.current_user_list) == 0:
                    exit_select_button.config(state=tk.DISABLED)
                else:
                    exit_select_button.config(state=tk.NORMAL)
            case "全选":
                def select_all_button_clicked():
                    # 创建样式
                    style = ttk.Style()
                    style.configure('Blue.TFrame', background='#00BFFF')

                    for c in constant.content_frame.winfo_children():
                        if c not in constant.selected_list:
                            constant.selected_list.append(c)

                        c.configure(style='Blue.TFrame')

                        for kid in c.winfo_children():
                            kid.config(bg="#00BFFF")

                    if isinstance(constant.delete_button, ttk.Button):
                        constant.delete_button.configure(state=tk.NORMAL)

                # 全选按钮
                select_all_button = ttk.Button(constant.toolbar_frame, text="全选",
                                               command=select_all_button_clicked)
                select_all_button.pack(side="left", expand=True, fill="both")
                if len(constant.current_user_list) == 0:
                    select_all_button.config(state=tk.DISABLED)
                else:
                    select_all_button.config(state=tk.NORMAL)
            case "反选":
                def invert_select_button_clicked():
                    # 创建样式
                    style = ttk.Style()
                    style.configure('Blue.TFrame', background='#00BFFF')

                    for c in constant.content_frame.winfo_children():
                        if c not in constant.selected_list:
                            constant.selected_list.append(c)

                            c.configure(style='Blue.TFrame')

                            for kid in c.winfo_children():
                                kid.config(bg="#00BFFF")
                        else:
                            constant.selected_list.remove(c)

                            c.configure(style='TFrame')

                            for kid in c.winfo_children():
                                kid.config(bg="SystemButtonFace")

                    if len(constant.selected_list) != 0:
                        if isinstance(constant.delete_button, ttk.Button):
                            constant.delete_button.config(state=tk.NORMAL)
                    else:
                        if isinstance(constant.delete_button, ttk.Button):
                            constant.delete_button.config(state=tk.DISABLED)

                # 反选按钮
                invert_select_button = ttk.Button(constant.toolbar_frame, text="反选",
                                                  command=invert_select_button_clicked)
                invert_select_button.pack(side="left", expand=True, fill="both")
                if len(constant.current_user_list) == 0:
                    invert_select_button.config(state=tk.DISABLED)
                else:
                    invert_select_button.config(state=tk.NORMAL)
            case "删除":
                def delete_button_clicked():
                    selected_user = []

                    for father_label in constant.selected_list:
                        title = father_label.winfo_children()[0].cget("text")
                        username = father_label.winfo_children()[1].cget("text")
                        password = father_label.winfo_children()[2].cget("text")
                        note = father_label.winfo_children()[3].cget("text")

                        selected_user.append(User(title, username, password, note))

                    constant.selected_list = []

                    for user in constant.current_user_list:
                        if user in selected_user:
                            constant.current_user_list.remove(user)

                    flash_interface(constant.app_mode[2])

                # 删除按钮
                constant.delete_button = ttk.Button(constant.toolbar_frame, text="删除",
                                                    command=delete_button_clicked)
                constant.delete_button.pack(side="left", expand=True, fill="both")
                if len(constant.selected_list) == 0:
                    constant.delete_button.config(state=tk.DISABLED)
                else:
                    constant.delete_button.config(state=tk.NORMAL)
            case "退出选择":

                # 退出选择按钮
                exit_select_button = ttk.Button(constant.toolbar_frame, text="退出选择",
                                                command=lambda: flash_interface(constant.app_mode[0]))
                exit_select_button.pack(side="left", expand=True, fill="both")
            case "退出搜索":
                # 退出搜索按钮
                exit_search_button = ttk.Button(constant.toolbar_frame, text="退出搜索",
                                                command=lambda: flash_interface(constant.app_mode[0]))
                exit_search_button.pack(side="left", expand=True, fill="both")


def set_content_frame(show_list):
    if len(constant.content_frame.winfo_children()) != 0:
        for child in constant.content_frame.winfo_children():
            child.destroy()

    if len(show_list) == 0:
        label = tk.Label(constant.content_frame, text="没有内容用于展示", background="white", foreground="red",
                         font=("新宋体", 20, "bold"))
        label.pack(fill="both", expand=True)
    else:
        for i in range(len(show_list)):
            row = ttk.Label(constant.content_frame)
            row.place(relx=0, rely=i * 0.125, relheight=0.125, relwidth=1)

            l0 = tk.Label(row, text=show_list[i].title)
            l0.place(relx=0, rely=0, relheight=1, relwidth=0.2)

            l1 = tk.Label(row, text=show_list[i].username)
            l1.place(relx=.2, rely=0, relheight=1, relwidth=0.2)

            l2 = tk.Label(row, text=show_list[i].password)
            l2.place(relx=0.4, rely=0, relheight=1, relwidth=0.2)

            l3 = tk.Label(row, text=show_list[i].note)
            l3.place(relx=0.6, rely=0, relheight=1, relwidth=0.4)


def decrypt_zip(zip_path, password, extract_to=None):
    try:
        if extract_to is None:
            extract_to = os.path.dirname(zip_path)

        with pyzipper.AESZipFile(zip_path) as zipf:
            zipf.setpassword(password.encode('utf-8'))
            zipf.extractall(extract_to)
            # print(f"文件已成功解压到 {extract_to}")
        filename = os.path.basename(zip_path).split('.')[0] + '.txt'
        # 返回解压后的txt文件路径
        return os.path.join(extract_to, filename)

    except Exception:
        messagebox.showinfo(message="解压文件时出错")
        return None


def create_2_buttons(container):
    def new_button_click():
        nonlocal container
        container.destroy()

        root = tk.Tk()

        # 设置标题
        root.title("密码管理器")

        # 设置大小
        set_window_size_pos(root, 600, 400)

        # 置顶显示
        root.attributes("-topmost", True)

        # 创建工具栏容器
        constant.toolbar_frame = ttk.Frame(root)
        constant.toolbar_frame.place(relx=0, rely=0, relheight=0.1, relwidth=1)

        # 创建表头容器
        constant.header_frame = ttk.Frame(root)
        constant.header_frame.place(relx=0, rely=0.1, relheight=0.1, relwidth=1)

        # 创建内容展示区容器
        constant.content_frame = ttk.Frame(root)
        constant.content_frame.place(relx=0, rely=0.2, relheight=0.8, relwidth=1)

        set_header_frame()

        set_toolbar_frame(constant.main_button_list)

        set_content_frame(constant.current_user_list)

        root.mainloop()

    # 新建按钮，用于创建新的json文件
    new_button = ttk.Button(
        container,
        text='新建',
        command=lambda: new_button_click()
    )
    new_button.place(relx=0, rely=0, relwidth=0.5, relheight=1)

    def open_button_click():
        nonlocal container
        container.withdraw()

        file_path = askopenfilename(filetypes=[("ZIP files", "*.zip")])
        user_list = []
        if file_path:
            txt_path = decrypt_zip(file_path, constant.zip_password)
            if txt_path:
                with open(txt_path) as txt_file:
                    data_list = txt_file.readlines()
                os.remove(txt_path)
                for data in data_list:
                    if len(data.strip()) > 0:
                        line_list = data.split(',')
                        title = line_list[0].strip()
                        username = line_list[1].strip()
                        password = line_list[2].strip()
                        note = line_list[3].strip()
                        user_list.append(User(title, username, password, note))
                container.destroy()
                # 按用户名排序
                user_list.sort(key=lambda x: x.title)
                constant.current_user_list = user_list

                root = tk.Tk()

                # 设置标题
                root.title("密码管理器")

                # 设置大小
                set_window_size_pos(root, 600, 400)

                # 置顶显示
                root.attributes("-topmost", True)

                # 创建工具栏容器
                constant.toolbar_frame = ttk.Frame(root)
                constant.toolbar_frame.place(relx=0, rely=0, relheight=0.1, relwidth=1)

                # 创建表头容器
                constant.header_frame = ttk.Frame(root)
                constant.header_frame.place(relx=0, rely=0.1, relheight=0.1, relwidth=1)

                # 创建内容展示区容器
                constant.content_frame = ttk.Frame(root)
                constant.content_frame.place(relx=0, rely=0.2, relheight=0.8, relwidth=1)

                set_header_frame()

                set_toolbar_frame(constant.main_button_list)

                set_content_frame(constant.current_user_list)

                root.mainloop()
            else:
                messagebox.showinfo(message="出现意外情况，打开失败")
                container.deiconify()
        else:
            messagebox.showinfo(message="你未选择任何文件。")
            container.deiconify()

    # 打开按钮，用于打开json文件
    open_button = ttk.Button(
        container,
        text='打开',
        command=open_button_click
    )
    open_button.place(relx=0.5, rely=0, relwidth=0.5, relheight=1)


def set_header_frame():
    title_label = tk.Label(constant.header_frame, text="标题")
    title_label.place(relx=0, rely=0, relheight=1, relwidth=0.2)

    username_label = tk.Label(constant.header_frame, text="用户名")
    username_label.place(relx=0.2, rely=0, relheight=1, relwidth=0.2)

    password_label = tk.Label(constant.header_frame, text="密码")
    password_label.place(relx=0.4, rely=0, relheight=1, relwidth=0.2)

    note_label = tk.Label(constant.header_frame, text="备注")
    note_label.place(relx=0.6, rely=0, relheight=1, relwidth=0.4)


def set_window_size_pos(container, window_width, window_height):
    # 设置窗口大小，位置居中

    # 获取屏幕尺寸
    screen_width = container.winfo_screenwidth()
    screen_height = container.winfo_screenheight()

    # 找到中心点
    center_x = int(screen_width / 2 - window_width / 2)
    center_y = int(screen_height / 2 - window_height / 2)

    # 将窗口的位置设置为屏幕的中心
    container.geometry(f'{window_width}x{window_height}+{center_x}+{center_y}')

    # 设置窗口不可拉伸
    container.resizable(width=False, height=False)


if __name__ == '__main__':
    # 保存时，对先把内容写入txt，再把txt加密压缩为zip文件
    run()
